#include "qtchatclient.h"

#include "netservice.h"
#include "netchatmsg.h"

#include <boost/asio.hpp>
#include <boost/bind.hpp>

#include <iostream>

QtNetClient::QtNetClient(endPointIteratorType endpoint_iterator)
    : m_socket(NetService::IOService().getIOService())
{
    boost::asio::async_connect(m_socket, endpoint_iterator,
        boost::bind(&QtNetClient::handle_connect, this,
            boost::asio::placeholders::error));
}

void QtNetClient::write(const NetChatMsg& a_msg)
{
    NetService::IOService().getIOService().post(boost::bind(&QtNetClient::do_write, this, a_msg));
}

void QtNetClient::close()
{
    NetService::IOService().getIOService().post(boost::bind(&QtNetClient::do_close, this));
}

void QtNetClient::handle_connect(const boost::system::error_code& a_error)
{
  if (!a_error)
  {
    boost::asio::async_read(m_socket,
        boost::asio::buffer(m_read_msg.data(), NetChatMsg::header_length),
           boost::bind(&QtNetClient::handle_read_header, this,
               boost::asio::placeholders::error));
  }
}

void QtNetClient::handle_read_header(const boost::system::error_code& error)
{
   if (!error && m_read_msg.decode_header())
   {
       boost::asio::async_read(m_socket,
           boost::asio::buffer(m_read_msg.body(), m_read_msg.body_length()),
               boost::bind(&QtNetClient::handle_read_body, this,
                   boost::asio::placeholders::error));
   }
   else
   {
       do_close();
   }
}

void QtNetClient::handle_read_body(const boost::system::error_code& error)
{
   if (!error)
   {
       QString l_message(m_read_msg.body());
       emit receivedMsg(l_message.left(m_read_msg.body_length()));
       boost::asio::async_read(m_socket,
           boost::asio::buffer(m_read_msg.data(), NetChatMsg::header_length),
               boost::bind(&QtNetClient::handle_read_header, this,
                   boost::asio::placeholders::error));
   }
   else
   {
       do_close();
   }
}

void QtNetClient::do_write(NetChatMsg msg)
{
   bool write_in_progress = !m_write_msgs.empty();
   m_write_msgs.push_back(msg);
   if (!write_in_progress)
   {
       boost::asio::async_write(m_socket,
           boost::asio::buffer(m_write_msgs.front().data(),
               m_write_msgs.front().length()),
                   boost::bind(&QtNetClient::handle_write, this,
                       boost::asio::placeholders::error));
   }
}

void QtNetClient::handle_write(const boost::system::error_code& error)
{
   if (!error)
   {
       m_write_msgs.pop_front();
       if (!m_write_msgs.empty())
       {
           boost::asio::async_write(m_socket,
               boost::asio::buffer(m_write_msgs.front().data(),
                   m_write_msgs.front().length()),
                       boost::bind(&QtNetClient::handle_write, this,
                           boost::asio::placeholders::error));
       }
   }
   else
   {
       do_close();
   }
}

void QtNetClient::do_close()
{
    m_socket.close();
}

QtChatClient::QtChatClient()
    : m_client()
    , m_pseudo("unknown chatter")
{

}

QtChatClient::~QtChatClient()
{
    if( m_client.get() ) {
        m_client->close();
        m_async.join();
        m_client.reset();
        NetService::IOService().getIOService().stop();
    }
}

void QtChatClient::setPseudonyme(const QString &a_pseudo)
{
    emit information(QString("%1 changed is pseudo for %2").arg(m_pseudo).arg(a_pseudo));
    m_pseudo = a_pseudo;
}

void QtChatClient::sendStringMsg(const QString &a_msg)
{
    if( m_client.get() ) {
        QString l_full_message(QString("%1 : %2").arg(m_pseudo).arg(a_msg));

        NetChatMsg msg;
        msg.body_length(std::strlen(l_full_message.toStdString().c_str()));
        std::memcpy(msg.body(), l_full_message.toStdString().c_str(), msg.body_length());
        msg.encode_header();
        m_client->write(msg);
    }
    else {
        emit information(QString("[ERROR] : You are not connected to any server"));
    }
}

void QtChatClient::connectServer(const QString &a_host,const QString &a_port)
{
    if( m_client.get() ) {
        m_client->close();
        m_async.join();
        m_client.reset();
        NetService::IOService().getIOService().stop();
    }

    emit information(QString("Connection at %1@%2").arg(a_host).arg(a_port));

    typedef boost::asio::ip::tcp::resolver ResolverType;
    ResolverType resolver(NetService::IOService().getIOService());
    ResolverType::query query(a_host.toStdString().c_str(),a_port.toStdString().c_str());
    ResolverType::iterator iterator = resolver.resolve(query);

    m_client.reset(new QtNetClient(iterator));
    connect(m_client.get(),SIGNAL(receivedMsg(const QString &)),this, SLOT(getMsgFromClient(const QString &)));
    m_async = std::thread(NetService::run);
}

void QtChatClient::disconnectServer()
{
    if( m_client.get() ) {
        m_client->close();
        m_async.join();
        m_client.reset();
        NetService::IOService().getIOService().stop();
    }
    emit information(QString("You are disconnected"));
}

void QtChatClient::getMsgFromClient(const QString &a_msg)
{
    emit newStringMsg(a_msg);
}

